<?php
/**
 * Created by PhpStorm.
 * User: Administrator
 * Date: 2020/10/11
 * Time: 17:31
 */

namespace app\model;

use think\facade\Config;
use think\facade\Db;
use think\facade\Env;
use think\facade\Request;
use think\Model;

class Token extends Model{
    protected $table = 'user_token';
    protected $pk = 'token_id';

    protected $tokenConfig;

    protected static function init(){
        //TODO:初始化内容
    }

    public function __construct(){
        // 调用父构造函数
        parent::__construct();
        // 获取 Token 配置
        $config = Config::get('token');

        if (!$config || !isset($config['server']) || !$config['server']) {
            fail('token机制运行失败，请联系管理员');
        }
        $this->tokenConfig = $config;
        // 删除过期 Token
        $this->unsetExpireToken();
    }

    /**
     * 生成 token
     * @param $uid
     * @return bool|string
     */
    public function generateToken($uid){
        // 获取时间戳
        $time = time();
        // 获取IP
        $ip = Request::ip();
        // Token服务已开启
        if ($this->tokenConfig['status']) {
            // 验证 IP 黑名单，黑名单的IP禁止获取到Token
            if (in_array($ip, $this->tokenConfig['blacklist'])) {
                fail('非法访问IP');// 非法访问IP
                return false;
            }
        }
        // Token服务已关闭
        else {
            // 验证 IP 白名单，白名单的IP才能获取到Token
            if (!in_array($ip, $this->tokenConfig['whitelist'])) {
                fail('Token 服务关闭');// Token 服务关闭
                return false;
            }
        }
        // 删除用户Token
        if (Env::get('APP_ENV') == 'online') {// 正式服只允许一个用户登录
            $where = [
                ['uid', '=', $uid],
            ];
            $this->deleteToken($where);
        }
        // 从新获取Token
        $tryNum = 0;
        do {
            $tryNum ++;
            $token = md5(uniqid(mt_rand(),true));
            if ($tryNum > $this->tokenConfig['generate_times']){
                fail('token创建失败');// Token 创建失败
                return false;
            }
        } while ($this->readToken([['token', '=', $token]]));
        // 保存Token
        $data = [
            'uid' => $uid,
            'ip' => $ip,
            'token' => $token,
            'create_time' => $time,
            'expire_time' => $time + $this->tokenConfig['period'],
        ];
        if (!$this->createToken($data)) {
            fail('token保存失败');// Token 保存失败
            return false;
        }
        return $token;
    }
    /**
     * 验证 Token
     *
     */
    public function verifyToken($token){
        // 获取时间戳
        $time = time();
        // 请求参数
        $controller = strtolower(request()->controller());
        $action = strtolower(request()->action());
//        $token = request()->header('token');
        // 不进行Token验证得控制器和方法
        $configUnverifyOp = $this->tokenConfig['unverify'];

//        logend(array($controller,$action,$configUnverifyOp));
        $arr = array();
        if(!empty($configUnverifyOp)){
            foreach ($configUnverifyOp as $op){
                if (isset($op[$controller]) && (!empty($op[$controller]) || in_array($action, $op[$controller]))) {
                    return true;
                }
            }
        }
        // 验证 token 是否为空
        if (!$token) {
            fail('token 不能为空！',[],401);// Token 不能为空
            return false;
        }
        // 获取 token 信息
        $tokenInfo = $this->readToken([['token','=',$token]]);
        // Token 获取失败
        if (!$tokenInfo) {
            fail('token 不存在',[],401);// Token 不存在
            return false;
        }
        // Token 未绑定用户
        if (!$tokenInfo['uid']) {
            // 删除Token
            $this->deleteToken([['token_id','=',$tokenInfo['token_id']]]);
            fail('token 出错',[],401);// Token 未绑定用户
            return false;
        }
        // Token服务已开启
        if ($this->tokenConfig['status']) {
            // 验证 IP 黑名单，黑名单的IP禁止获取到Token
            if (in_array($tokenInfo['ip'], $this->tokenConfig['blacklist'])) {
                // 删除Token
                $this->deleteToken([['token_id','=',$tokenInfo['token_id']]]);
                fail('token 非法访问IP',[],401);// Token 非法访问IP
                return false;
            }
        }
        // Token服务已关闭
        else {
            // 验证 IP 白名单，白名单的IP才能获取到Token
            if (!in_array($tokenInfo['ip'], $this->tokenConfig['whitelist'])) {
                fail('token 服务关闭',[],401);
                return false;
            }
        }
        // 验证是否过期
        if ($tokenInfo['expire_time'] <= $time) {
            // 删除Token
            $this->deleteToken([['token_id','=',$tokenInfo['token_id']]]);
            fail('token 已过期',[],401);
            return false;
        }
        // 更新 Token 访问时间
        $this->updateToken([['token_id', '=', $tokenInfo['token_id']]], ['update_time' => $time]);
        // 返回验证成功的用户ID
        return $tokenInfo['uid'];
    }
    /**
     * 删除 Token
     *
     */
    public function unsetUserToken($uid){
        $where = [
            ['wx_uid', '=', $uid],
        ];
        return $this->deleteToken($where);
    }
    /**
     * 自动回收过期 Token
     *
     */
    private function unsetExpireToken(){
        // 启用删除几率，默认为 1/100
        $percentage = (isset($this->config['recovery_probability']) && $this->config['recovery_probability']) ? $this->config['recovery_probability'] : 100;
        if (mt_rand(1, $percentage) == 1) {
            $where  = [
                ['expire_time', '<=', time()],
            ];
            $this->deleteToken($where);
        }
        //
        return true;
    }
    /**
     * 新增 Token
     *
     */
    private function createToken($data){
        return $this->insert($data);
    }
    /**
     * 更新 Token
     *
     */
    private function updateToken($where, $data){
        return $this->where($where)->update($data);
    }
    /**
     * 获取 Token 详情
     *
     */
    private function readToken($where){
        return Db::table('user_token')->where($where)->find();
    }

    /**
     * 删除 Token
     *
     */
    private function deleteToken($where){
        return $this->where($where)->delete();
    }

}